package preProcessTrace;

import utils.Utils;

import java.io.*;
import java.util.Scanner;

public class PreProcessTraces {
    /**通过trace的操作关键字判断一行trace是不是我们需要的trace
     * @param line 待判断的trace行
     * @return 如果是我们需要的trace返回true，不是则返回false
     */
    public static boolean isNeededDslTrace(String line) {
        String[] uselessTraceCommands = {"Del", "Add Class root", "Add Set root"};
        String lineCommand = Utils.RegularExpressionProcess(line, "\\[gc,heap\\s+\\] (.*?) (H|H1):");
        // 单独处理 NewM
        // [0.313s][info][gc,heap     ] NewM C:[[Ljava/lang/invoke/LambdaForm$Name;, D:5 10 , T:main, M:java/lang/invoke/LambdaForm.<clinit> ()V
        if (lineCommand == null) {
            // NewM的例子
            // NewM C:[[Ljava/lang/invoke/LambdaForm$Name;, D:5 10 , T:main, M:java/lang/invoke/LambdaForm.<clinit> ()V
            String newMClassName = Utils.RegularExpressionProcess(line, "\\[gc,heap\\s+\\] NewM C:(.*?),"); // 获得NewM中的Class名称
            if (newMClassName != null) {
                // 表示这行语句是NewM
                return true;
            }
        } else {
            // 过滤掉我们不需要的trace操作
            for (String uselessCommand : uselessTraceCommands) {
                if (lineCommand.contains(uselessCommand)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 初始的trace序列包含GC日志和我们定义的DSL语句，该函数主要有以下功能
     * 1. 清洗trace，去除GC日志部分
     * 2. 截取trace，只保留应用程序main函数部分
     * 3. 删除我们定义trace语句中不想要的trace语句，具体见isNeededDslTrace函数
     *
     * @param srcFilePath 源trace文件路径
     * @param dstFilePath 处理后的trace文件路径
     * @throws FileNotFoundException
     */
    public static void refineTraceRecords(String srcFilePath, String dstFilePath) throws FileNotFoundException {
        // 判断dstFilePath文件是否存在，如果存在，则删除
        File file = new File(dstFilePath);
        if (file.exists()) {
            file.delete();
        }

        Scanner scanner = new Scanner(new FileReader(srcFilePath));
        boolean isMain = false; // 表示当前的trace是不是在main函数的范围内
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
//            System.out.println(line);
            // 判断line是不是main函数开始
            // main函数开始trace例子 [32.093s][info][gc,heap     ] Add PR H:0x000000004ac5dcd8, C:[Ljava/lang/String;, T:main, M:Test.main ([Ljava/lang/String;)V
            if (Utils.isInputContainsPattern(line, "Add PR H:.*?, C:\\[Ljava/lang/String;, T:main, M:Test\\.main \\(\\[Ljava/lang/String;\\)V")) {
                // 如果这行内容是main函数开始的trace
                isMain = true;
            }
            // 判断line是不是main函数的结束
            // main函数结束trace例子 [33.324s][info][gc,heap     ] Add PR H:0x0000000050040f0c, C:java/lang/Thread, T:main, M:java/lang/Thread.exit ()V
            if (Utils.isInputContainsPattern(line, "Add PR H:.*?, C:java/lang/Thread, T:main, M:java/lang/Thread\\.exit \\(\\)V")) {
                isMain = false;
            }

            if (isMain) {
                // 如果是main函数范围的trace，才进行处理
                // 处理主要是区分GC trace还是我们自定义DSL trace
                //[0.020s][info][gc,heap     ] Get H1:0x000000004dc63996, C1:java/lang/Module, IS:true, H2:0x00000000179d3b25, C2:java/lang/Module, F:ALL_UNNAMED_MODULE, T:main, M:java/lang/Module.<clinit> ()V
                if (Utils.isInputContainsPattern(line, "\\[gc,heap\\s+\\] (New|NewM|NewL|PutA|Put|Add|Del|Rep|Return|Get)")) {
                    // 是我们自定义的trace
                    // 获得这一行trace的操作名称
                    String lineComand = Utils.RegularExpressionProcess(line, "\\[gc,heap\\s+\\] (.*?) (H|H1):");
                    // 下面的if语句主要处理 NewM开头的trace，因为NewM开头的trace里面没有H或H1
                    if (lineComand == null) {
                        // NewM的例子
                        // NewM C:[[Ljava/lang/invoke/LambdaForm$Name;, D:5 10 , T:main, M:java/lang/invoke/LambdaForm.<clinit> ()V
                        String newMClassName = Utils.RegularExpressionProcess(line, "\\[gc,heap\\s+\\] NewM C:(.*?),"); // 获得NewM中的Class名称
                        if (newMClassName != null) {
                            // 表示这行语句是NewM
                            Utils.writeLineToDstFile(line, dstFilePath);
                        }
                    } else {
                        // 该trace是我们需要的自定义DSL trace
                        if (isNeededDslTrace(line)) {
                            // 写入到dstFilePath文件中
                            Utils.writeLineToDstFile(line, dstFilePath);
                        }
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws FileNotFoundException {
        // 源trace文件所在的路径
//        String srcFilePath = "traces.txt";
//        String srcFilePath = "result.txt";
//        String srcFilePath = "methodTraces.txt";
//        String FilePath = "src/test/resources/dslProgramGen/inputRaw/rawRefinedTraces_";
        String srcFilePath = "src/test/resources/dslProgramGen/inputRaw/Test.txt" ;
        // trace清洗后的内容所在的文件路径
        String dstFilePath = "src/test/resources/dslProgramGen/inputRaw/Get.txt";

        long startTime = System.currentTimeMillis();
//        for (int i = 1; i <= 46 ; i++) {
//            srcFilePath = FilePath + i + ".txt";
//            dstFilePath = FilePath + i + "a.txt";
//            refineTraceRecords(srcFilePath, dstFilePath);
//            System.out.println("OK"+i);
//        }
        refineTraceRecords(srcFilePath,dstFilePath);
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;
        System.out.println("用时: " + executionTime / 1000.0 / 3600.0 + "小时");
    }
}
